home *** CD-ROM | disk | FTP | other *** search
/ SGI Freeware 1999 August / SGI Freeware 1999 August.iso / dist / fw_xemacs.idb / usr / freeware / lib / xemacs-20.4 / lisp / packages / igrep.el.z / igrep.el
Encoding:
Text File  |  1998-05-21  |  29.5 KB  |  824 lines

  1. ;;;; igrep.el --- An improved interface to `grep`.
  2.  
  3. ;;; Description:
  4. ;;; 
  5. ;;; Define the \[igrep] command, which is like \[grep] except that it
  6. ;;; takes three required arguments (PROGRAM, EXPRESSION, and FILES) and
  7. ;;; an optional argument (OPTIONS) instead of just one argument (COMMAND).
  8. ;;; Also define the analogous \[egrep] and \[fgrep] commands for convenience.
  9. ;;; 
  10. ;;; Define the \[igrep-find] command, which is like \[igrep] except that
  11. ;;; it uses `find` to recursively `grep` a directory.  Also define the
  12. ;;; analogous \[egrep-find] and \[fgrep-find] commands for convenience.
  13. ;;; 
  14. ;;; \[igrep] and \[igrep-find] (and their analogues) provide defaults
  15. ;;; for the EXPRESSION and FILES arguments when called interactively,
  16. ;;; and there are global variables that control the syntax of the `grep`
  17. ;;; and `find` shell commands that are executed.  A user option controls
  18. ;;; whether the corresponding GNU (gzip) "zPROGRAM" script is used, to
  19. ;;; `grep` compressed files.
  20. ;;; 
  21. ;;; \[agrep] and \[agrep-find] are also defined as convenient interfaces
  22. ;;; to the approximate `grep` utility, which is distributed with the
  23. ;;; `glimpse' indexing and query tool (available from
  24. ;;; <URL:http://glimpse.cs.arizona.edu:1994/>).
  25. ;;; 
  26. ;;; \[grep] itself is advised to provide the \[igrep] interface when
  27. ;;; called interactively (when called programmatically, it still uses
  28. ;;; the original argument list).  \[grep-find] is defined as an alias
  29. ;;; for \[igrep-find].
  30. ;;; 
  31. ;;; When run interactively from Dired mode, the various \[igrep]
  32. ;;; commands provide defaults for the EXPRESSION and FILES arguments
  33. ;;; that are based on the visited directory (including any inserted
  34. ;;; subdirectories) and the current file.  In addition, the
  35. ;;; \[dired-do-igrep] and \[dired-do-igrep-find] commands are defined
  36. ;;; that respect the `dired-do-*' command calling conventions: a prefix
  37. ;;; argument is interpreted as the number of succeeding files to `grep`,
  38. ;;; otherwise all the marked files are `grep`ed.  \[dired-do-grep] and
  39. ;;; \[dired-do-grep-find] are defined as aliases for \[dired-do-igrep]
  40. ;;; and \[dired-do-igrep-find].
  41.  
  42. ;;; Copyright:
  43. ;;; 
  44. ;;; Copyright ⌐ 1994,1995,1996,1997 Kevin Rodgers
  45. ;;; 
  46. ;;; This program is free software; you can redistribute it and/or modify
  47. ;;; it under the terms of the GNU General Public License as published by
  48. ;;; the Free Software Foundation; either version 2 of the License, or
  49. ;;; at your option) any later version.
  50. ;;; 
  51. ;;; This program is distributed in the hope that it will be useful,
  52. ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  53. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  54. ;;; GNU General Public License for more details.
  55. ;;; 
  56. ;;; You should have received a copy of the GNU General Public License
  57. ;;; along with this program; if not, write to the Free Software
  58. ;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  59. ;;; 
  60. ;;; Neither my former nor current employer (Martin Marietta and
  61. ;;; Information Handling Services, respectively) has disclaimed any
  62. ;;; copyright interest in igrep.el.
  63. ;;; 
  64. ;;; Kevin Rodgers <kevinr@ihs.com>        Project Engineer
  65. ;;; Information Handling Services        Electronic Systems Development
  66. ;;; 15 Inverness Way East, M/S A201        
  67. ;;; Englewood CO 80112 USA            (303)397-2807[voice]/-2779[fax]
  68.  
  69. ;;; Installation:
  70. ;;; 
  71. ;;; 1. Put this file in a directory that is a member of load-path, and
  72. ;;;    byte-compile it (e.g. with `M-x byte-compile-file') for better
  73. ;;;    performance.  You can ignore any warnings about references to free
  74. ;;;    variables and "not known to be defined" functions.
  75. ;;; 2. Put these forms in default.el or ~/.emacs:
  76. ;;;    (autoload (function igrep) "igrep"
  77. ;;;       "*Run `grep` PROGRAM to match EXPRESSION in FILES..." t)
  78. ;;;    (autoload (function igrep-find) "igrep"
  79. ;;;       "*Run `grep` via `find`..." t)
  80. ;;;    (autoload (function dired-do-igrep) "igrep"
  81. ;;;       "*Run `grep` on the marked (or next prefix ARG) files." t)
  82. ;;;    (autoload (function dired-do-igrep-find) "igrep"
  83. ;;;       "*Run `grep` via `find` on the marked (or next prefix ARG) directories." t)
  84. ;;; 2. a. For completeness, you can add these forms as well:
  85. ;;;    (autoload (function grep) "igrep"
  86. ;;;       "*Run `grep` PROGRAM to match EXPRESSION in FILES..." t)
  87. ;;;    (autoload (function egrep) "igrep"
  88. ;;;       "*Run `egrep`..." t)
  89. ;;;    (autoload (function fgrep) "igrep"
  90. ;;;       "*Run `fgrep`..." t)
  91. ;;;    (autoload (function agrep) "igrep"
  92. ;;;       "*Run `agrep`..." t)
  93. ;;;    (autoload (function grep-find) "igrep"
  94. ;;;       "*Run `grep` via `find`..." t)
  95. ;;;    (autoload (function egrep-find) "igrep"
  96. ;;;       "*Run `egrep` via `find`..." t)
  97. ;;;    (autoload (function fgrep-find) "igrep"
  98. ;;;       "*Run `fgrep` via `find`..." t)
  99. ;;;    (autoload (function agrep-find) "igrep"
  100. ;;;       "*Run `agrep` via `find`..." t)
  101. ;;;    (autoload (function dired-do-grep) "igrep"
  102. ;;;       "*Run `grep` on the marked (or next prefix ARG) files." t)
  103. ;;;    (autoload (function dired-do-grep-find) "igrep"
  104. ;;;       "*Run `grep` via `find` on the marked (or next prefix ARG) directories." t)
  105. ;;; 3. If you are running Windows 95/NT, you should install findutils
  106. ;;;    and grep from release 17.1 (or higher) of the Cygnus GNU-Win32
  107. ;;;    distribution.  See <URL:http://www.cygnus.com/misc/gnu-win32/>.
  108.  
  109. ;;; Usage:
  110. ;;; 
  111. ;;; M-x igrep            M-x igrep-find
  112. ;;; M-x  grep            M-x  grep-find
  113. ;;; M-x egrep            M-x egrep-find
  114. ;;; M-x fgrep            M-x fgrep-find
  115. ;;; M-x agrep            M-x agrep-find
  116. ;;; M-x dired-do-igrep        M-x dired-do-igrep-find
  117. ;;; M-x  dired-do-grep        M-x  dired-do-grep-find
  118. ;;; (Each of the 10 igrep commands accepts 1, 2, or 3 `C-u' prefix arguments.
  119. ;;; The 2 Dired commands interpret a prefix argument like the regular `dired-do'
  120. ;;; commands.)
  121. ;;; C-x ` (M-x next-error)    C-c C-c (M-x compile-goto-error) [in *igrep*]
  122.  
  123. ;;; Customization examples:
  124. ;;; 
  125. ;;; To ignore case by default:
  126. ;;;     (setq igrep-options "-i")
  127. ;;; To search subdirectories by default:
  128. ;;;     (setq igrep-find t)
  129. ;;; To search files with the GNU (gzip) zgrep script:
  130. ;;;     (setq igrep-use-zgrep t)
  131. ;;; or define new igrep commands (this works for zegrep and zfgrep as well):
  132. ;;;     (igrep-define zgrep)        ; M-x zgrep
  133. ;;;     (igrep-find-define zgrep)    ; M-x zgrep-find
  134. ;;; To avoid exceeding some shells' limit on command argument length
  135. ;;; (this only works when grep'ing files in the current directory):
  136. ;;;     (setq igrep-find t
  137. ;;;           igrep-find-prune-option "\\! -name .")
  138.  
  139. ;;; To do:
  140. ;;; 
  141. ;;; 1. Delete the *-program variables (except for igrep-program) and
  142. ;;;    replace igrep-options with a table that maps igrep-program to the
  143. ;;;    appropriate options.
  144. ;;; 2. Generalize support for the -prune find clause (e.g. -fstype nfs).
  145. ;;; 3. Provide support for `glimpse`.
  146. ;;; 4. Add a menu interface.
  147.  
  148. ;;; LCD Archive Entry:
  149. ;;; 
  150. ;;; igrep|Kevin Rodgers|kevinr@ihs.com|
  151. ;;; An improved interface to grep/egrep/fgrep; plus recursive `find` versions.|
  152. ;;; 97/02/10|2.56|~/misc/igrep.el.Z|
  153.  
  154.  
  155. ;;; Package interface:
  156.  
  157. (provide 'igrep)
  158.  
  159. (require 'backquote)            ; igrep-with-default-in-history
  160. (require 'compile)            ; compile-internal and grep-regexp-
  161.                     ; alist
  162.  
  163. (eval-when-compile
  164.   (require 'dired))
  165.  
  166. (defconst igrep-version "2.56"
  167.   "Version of igrep.el")
  168.  
  169.  
  170. ;;; User options:
  171.  
  172. (defgroup igrep nil
  173.   "An improved interface to `grep'."
  174.   :group 'processes)
  175.  
  176. (defcustom igrep-options nil
  177.   "*The options passed by \\[igrep] to `igrep-program', or nil.
  178.  
  179. `-n' will automatically be passed to `igrep-program', to generate the
  180. output expected by \\[next-error] and \\[compile-goto-error].
  181. `-e' will automatically be passed to `igrep-program', if it supports
  182. that option."
  183.   :type '(repeat (string :tag "Option"))
  184.   :group 'igrep)
  185.  
  186. (defcustom igrep-read-options nil
  187.   "*If non-nil, `\\[igrep]' always prompts for options;
  188. otherwise, it only prompts when 1 or 3 `C-u's are given as a prefix arg."
  189.   :type 'boolean
  190.   :group 'igrep)
  191.  
  192. (defcustom igrep-read-multiple-files nil
  193.   "*If non-nil, `\\[igrep]' always prompts for multiple-files;
  194. otherwise, it only prompts when 2 or 3 `C-u's are given as a prefix arg."
  195.   :type 'boolean
  196.   :group 'igrep)
  197.  
  198. (defcustom igrep-verbose-prompts t
  199.   "*If t, \\[igrep] prompts for arguments verbosely;
  200. if not t but non-nil, \\[igrep] prompts for arguments semi-verbosely;
  201. if nil, \\[igrep] prompts for arguments tersely."
  202.   :type 'boolean
  203.   :group 'igrep)
  204.  
  205. (defcustom igrep-save-buffers 'query
  206.   "*If t, \\[igrep] first saves each modified file buffer;
  207. if not t but non-nil, \\[igrep] offers to save each modified file buffer."
  208.   :type '(choice (const :tag "Save" t)
  209.          (const :tag "Dont Save" nil)
  210.          (const :tag "Query" query))
  211.   :group 'igrep)
  212.  
  213. (defvar igrep-program-table        ; referenced by igrep-use-zgrep
  214.   (let ((exec-directories exec-path)
  215.     (program-obarray (make-vector 11 0)))
  216.     (while exec-directories
  217.       (if (and (car exec-directories)
  218.            (file-directory-p (car exec-directories)))
  219.       (let ((grep-programs
  220.          (directory-files (car exec-directories)
  221.                   nil "grep\\(\\.exe\\)?\\'")))
  222.         (while grep-programs
  223.           ;; Check `(file-executable-p (car grep-programs))'?
  224.           (if (save-match-data
  225.             (string-match "\\.exe\\'" (car grep-programs)))
  226.           (intern (substring (car grep-programs) 0 -4) program-obarray)
  227.         (intern (car grep-programs) program-obarray))
  228.           (setq grep-programs (cdr grep-programs)))))
  229.       (setq exec-directories (cdr exec-directories)))
  230.     program-obarray)
  231.   "An obarray of available `grep` programs, passed by `igrep-read-program'
  232. to `completing-read' when `igrep-program' is nil.")
  233.  
  234. (defvar igrep-use-zgrep
  235.   (if (intern-soft "zgrep" igrep-program-table)
  236.       'files)
  237.   "If t, \\[igrep] searches files using the GNU (gzip) `zPROGRAM` script;
  238. If not t but non-nil, \\[igrep] searches compressed FILES using `zPROGRAM`;
  239. if nil, \\[igrep] searches files with `PROGRAM`.")
  240.  
  241.  
  242. ;;; User variables:
  243.  
  244. (defvar igrep-program "grep"
  245.   "The default shell program run by \\[igrep] and \\[igrep-find].
  246. It must take a `grep` expression argument and one or more file names.
  247. If nil, \\[igrep] prompts for the program to run.")
  248.  
  249. (defvar igrep-expression-quote-char
  250.   (if (memq system-type '(ms-dos windows-95 windows-nt emx))
  251.       ?\"
  252.     ?')
  253.   "The character used to delimit the EXPRESSION argument to \\[igrep],
  254. to protect it from shell filename expansion.")
  255.  
  256. (defvar igrep-expression-option
  257.   (if (or (eq system-type 'berkeley-unix)
  258.       (save-match-data
  259.         (string-match "-sco" system-configuration)))
  260.       "-e")
  261.   "If non-nil, the option used to specify the EXPRESSION argument to \\[igrep],
  262. to protect an initial `-' from option processing.")
  263.  
  264. (defvar igrep-parenthesis-escape-char
  265.   (if (memq system-type '(ms-dos windows-95 windows-nt emx))
  266.       nil
  267.     ?\\)
  268.   "If non-nil, the character used by \\[igrep] to escape parentheses,
  269. to protect them from shell interpretation.")
  270.  
  271. (defvar igrep-find nil
  272.   "If non-nil, \\[igrep] searches directories using `find`.
  273. See \\[igrep-find].")
  274.  
  275. (defvar igrep-find-prune-options
  276.   (if (not (save-match-data
  277.          (string-match "-sco" system-configuration)))
  278.       "-name SCCS -o -name RCS")
  279.   "The `find` clause used to prune directories, or nil;
  280. see \\[igrep-find].")
  281.  
  282. (defvar igrep-find-file-options "-type f -o -type l"
  283.   "The `find` clause used to filter files passed to `grep`, or nil;
  284. see \\[igrep-find].")
  285.  
  286. (defvar igrep-find-use-xargs
  287.   (if (equal (call-process "find" nil nil nil
  288.                (if (boundp 'grep-null-device)
  289.                    grep-null-device
  290.                  "/dev/null")
  291.                "-print0")
  292.          0)
  293.       'gnu)
  294.   "If `gnu', \\[igrep-find] executes
  295.     `find ... -print0 | xargs -0 -e grep ...`;
  296. if not `gnu' but non-nil, \\[igrep-find] executes
  297.     `find ... -print | xargs -e grep ...`;
  298. if nil, \\[igrep-find] executes
  299.     `find ... -exec grep ...`.")
  300.  
  301. (defvar igrep-program-default nil
  302.   "The default `grep` program, passed by `igrep-read-program'
  303. to `completing-read' when `igrep-program' is nil.")
  304.  
  305. (defvar igrep-expression-history '()
  306.   "The minibuffer history list for \\[igrep]'s EXPRESSION argument.")
  307.  
  308. (defvar igrep-files-history '()
  309.   "The minibuffer history list for \\[igrep]'s FILES argument.")
  310.  
  311.  
  312. ;;; Commands:
  313.  
  314. ;; This used to be evaluated at top level.  We consider it evil, so it
  315. ;; goes into a user-callable function.    --hniksic
  316. ;;;###autoload
  317. (defun igrep-insinuate ()
  318.   "Replace the `grep' functions with `igrep'."
  319.   (defadvice grep (around igrep-interface first (&rest grep-args) activate)
  320.     "If called interactively, use the \\[igrep] interface instead,
  321. where GREP-ARGS is (PROGRAM EXPRESSION FILES OPTIONS);
  322. if called programmatically, GREP-ARGS is still (COMMAND)."
  323.     (interactive (igrep-read-args))
  324.     (if (interactive-p)
  325.     (apply (function igrep) grep-args)
  326.       ad-do-it))
  327.   (defalias 'grep-find 'igrep-find)
  328.   (defalias 'dired-do-grep 'dired-do-igrep)
  329.   (defalias 'dired-do-grep-find 'dired-do-igrep-find))
  330.  
  331.  
  332. (defvar win32-quote-process-args)    ; XEmacs
  333.  
  334. ;;;###autoload
  335. (defun igrep (program expression files &optional options)
  336.   "*Run `grep` PROGRAM to match EXPRESSION in FILES.
  337. The output is displayed in the *igrep* buffer, which \\[next-error] and
  338. \\[compile-goto-error] parse to find each line of matched text.
  339.  
  340. PROGRAM may be nil, in which case it defaults to `igrep-program'.
  341.  
  342. EXPRESSION is automatically delimited by `igrep-expression-quote-char'.
  343.  
  344. FILES is either a file name pattern (expanded by the shell named by
  345. `shell-file-name') or a list of file name patterns.
  346.  
  347. Optional OPTIONS is also passed to PROGRAM; it defaults to `igrep-options'.
  348.  
  349. If a prefix argument \
  350. \(\\[universal-argument]\) \
  351. is given when called interactively,
  352. or if `igrep-read-options' is set, OPTIONS is read from the minibuffer.
  353.  
  354. If two prefix arguments \
  355. \(\\[universal-argument] \\[universal-argument]\) \
  356. are given when called interactively,
  357. or if `igrep-read-multiple-files' is set, FILES is read from the minibuffer
  358. multiple times.
  359.  
  360. If three prefix arguments \
  361. \(\\[universal-argument] \\[universal-argument] \\[universal-argument]\) \
  362. are given when called interactively,
  363. or if `igrep-read-options' and `igrep-read-multiple-files' are set,
  364. OPTIONS is read and FILES is read multiple times.
  365.  
  366. If `igrep-find' is non-nil, the directory or directories
  367. containing FILES is recursively searched for files whose name matches
  368. the file name component of FILES \(and whose contents match
  369. EXPRESSION\)."
  370.   (interactive
  371.    (igrep-read-args))
  372.   (if (null program)
  373.       (setq program (or igrep-program "grep")))
  374.   (if (null options)
  375.       (setq options igrep-options))
  376.   (if (not (listp files))        ; (stringp files)
  377.       (setq files (list files)))
  378.   (if (string-match "^[rj]?sh$" (file-name-nondirectory shell-file-name))
  379.       ;; (restricted, job-control, or standard) Bourne shell doesn't expand ~:
  380.       (setq files
  381.         (mapcar 'expand-file-name files)))
  382.   (let* ((win32-quote-process-args nil)    ; work around NT Emacs hack
  383.      (use-zgrep (cond ((eq igrep-use-zgrep t))
  384.               (igrep-use-zgrep
  385.                (let ((files files)
  386.                  (compressed-p nil))
  387.                  (while (and files (not compressed-p))
  388.                    (if (save-match-data
  389.                      (string-match "\\.g?[zZ]\\'" (car files)))
  390.                    (setq compressed-p t))
  391.                    (setq files (cdr files)))
  392.                  compressed-p))
  393.               (t nil)))
  394.      (command (format "%s -n %s %s %c%s%c %s %s"
  395.               (if (and use-zgrep
  396.                    (save-match-data
  397.                      (not (string-match "\\`z" program))))
  398.                   (setq program (concat "z" program))
  399.                 program)
  400.               (or options "")
  401.               (or igrep-expression-option
  402.                   (progn
  403.                 (if (save-match-data
  404.                       (string-match "\\`-" expression))
  405.                     (setq expression (concat "\\" expression)))
  406.                 ""))
  407.               igrep-expression-quote-char
  408.               expression
  409.               igrep-expression-quote-char
  410.               (if igrep-find
  411.                   (if igrep-find-use-xargs
  412.                   ""
  413.                 "\"{}\"")
  414.                 (mapconcat (function identity) files " "))
  415.               (if (boundp 'grep-null-device)
  416.                   grep-null-device
  417.                 "/dev/null"))))
  418.     (if igrep-find
  419.     (setq command
  420.           (igrep-format-find-command command files)))
  421.     (cond ((eq igrep-save-buffers t) (save-some-buffers t))
  422.       (igrep-save-buffers (save-some-buffers)))
  423.     (compile-internal command
  424.               (format "No more %c%s%c matches"
  425.                   igrep-expression-quote-char
  426.                   program
  427.                   igrep-expression-quote-char)
  428.               "igrep" nil grep-regexp-alist)))
  429.  
  430. ;; Analogue commands:
  431.  
  432. ;;;###autoload
  433. (defmacro igrep-define (analogue-command &rest igrep-bindings)
  434.   "Define ANALOGUE-COMMAND as an `igrep' analogue command.
  435. Optional (VARIABLE VALUE) arguments specify temporary bindings for the command."
  436. ;;;  (interactive "SCommand: ") ; C-u => read bindings?
  437.   (let ((analogue-program (symbol-name analogue-command)))
  438.     (` (defun (, analogue-command) (&rest igrep-args)
  439.      (, (format "*Run `%s` via \\[igrep].
  440. All arguments \(including prefix arguments, when called interactively\)
  441. are handled by `igrep'."
  442.             analogue-program))
  443.      (interactive
  444.       (let ((igrep-program (if igrep-program (, analogue-program)))
  445.         (igrep-program-default (, analogue-program)))
  446.         (igrep-read-args)))
  447.      (let ( (,@ igrep-bindings))
  448.        (apply (function igrep)
  449.           (cond ((interactive-p) (car igrep-args))
  450.             ((car igrep-args))
  451.             (t (, analogue-program)))
  452.           (cdr igrep-args)))))))
  453.  
  454. (igrep-define egrep)
  455. (igrep-define fgrep)
  456. (igrep-define agrep
  457.   (igrep-use-zgrep nil)
  458.   (igrep-expression-option "-e"))
  459.  
  460.  
  461. ;; Recursive (`find`) commands:
  462.  
  463. ;;;###autoload
  464. (defun igrep-find (&rest igrep-args)
  465.   "*Run `grep` via `find`; see \\[igrep] and `igrep-find'.
  466. All arguments \(including prefix arguments, when called interactively\)
  467. are handled by `igrep'."
  468.   (interactive
  469.    (let ((igrep-find t))
  470.      (igrep-read-args)))
  471.   (let ((igrep-find t))
  472.     (apply (function igrep) igrep-args)))
  473.  
  474. ;; Analogue recursive (`find`) commands:
  475.  
  476. ;;;###autoload
  477. (defmacro igrep-find-define (analogue-command &rest igrep-bindings)
  478.   "Define ANALOGUE-COMMAND-find as an `igrep' analogue `find` command.
  479. Optional (VARIABLE VALUE) arguments specify temporary bindings for the command."
  480. ;;;  (interactive "SCommand: ") ; C-u => read bindings?
  481.   (let ((analogue-program (symbol-name analogue-command)))
  482.     (setq analogue-command
  483.       (intern (format "%s-find" analogue-command)))
  484.     (` (defun (, analogue-command) (&rest igrep-args)
  485.      (, (format "*Run `%s` via \\[igrep-find].
  486. All arguments \(including prefix arguments, when called interactively\)
  487. are handled by `igrep'."
  488.             analogue-program))
  489.      (interactive
  490.       (let ((igrep-program (if igrep-program (, analogue-program)))
  491.         (igrep-program-default (, analogue-program))
  492.         (igrep-find t))
  493.         (igrep-read-args)))
  494.      (let ( (,@ igrep-bindings))
  495.        (apply (function igrep-find)
  496.           (cond ((interactive-p) (car igrep-args))
  497.             ((car igrep-args))
  498.             (t (, analogue-program)))
  499.           (cdr igrep-args)))))))
  500.  
  501. (igrep-find-define egrep)
  502. (igrep-find-define fgrep)
  503. (igrep-find-define agrep
  504.   (igrep-use-zgrep nil)
  505.   (igrep-expression-option "-e"))
  506.  
  507.  
  508. ;; Dired commands:
  509.  
  510. ;;;###autoload
  511. (defun dired-do-igrep (program expression &optional options arg)
  512.   "*Run `grep` PROGRAM to match EXPRESSION (with optional OPTIONS)
  513. on the marked (or next prefix ARG) files."
  514.   (interactive
  515.    (let* ((current-prefix-arg nil)
  516.       (igrep-args (igrep-read-args t)))
  517.      ;; Delete FILES:
  518.      (setcdr (nthcdr 1 igrep-args) (nthcdr 3 igrep-args))
  519.      ;; Append ARG:
  520.      (nconc igrep-args (list current-prefix-arg))))
  521.   (igrep program
  522.      expression
  523.      (funcall (cond ((fboundp 'dired-get-marked-files) ; GNU Emacs
  524.              'dired-get-marked-files)
  525.             ((fboundp 'dired-mark-get-files) ; XEmacs
  526.              'dired-mark-get-files))
  527.           t arg)
  528.      options))
  529.  
  530. ;;;###autoload
  531.  
  532. ;; Dired recursive (`find`) commands:
  533.  
  534. ;;;###autoload
  535. (defun dired-do-igrep-find (program expression &optional options arg)
  536.   "*Run `grep` PROGRAM to match EXPRESSION (with optional OPTIONS)
  537. on the marked (or next prefix ARG) directories."
  538.   (interactive
  539.    (let* ((current-prefix-arg nil)
  540.       (igrep-find t)
  541.       (igrep-args (igrep-read-args t)))
  542.      ;; Delete FILES:
  543.      (setcdr (nthcdr 1 igrep-args) (nthcdr 3 igrep-args))
  544.      ;; Append ARG:
  545.      (nconc igrep-args (list current-prefix-arg))))
  546.   (let ((igrep-find t))
  547.     (dired-do-igrep program expression options arg)))
  548.  
  549.  
  550. ;;; Utilities:
  551.  
  552. (defsubst igrep-file-directory (name)
  553.   ;; Return the directory component of NAME, or "." if it has no
  554.   ;; directory component.
  555.   (directory-file-name (or (file-name-directory name)
  556.                (file-name-as-directory "."))))
  557.  
  558. (defsubst igrep-file-pattern (name)
  559.   ;; Return the file component of NAME, or "*" if it has no file
  560.   ;; component.
  561.   (let ((pattern (file-name-nondirectory name)))
  562.        (if (string= pattern "")
  563.        "*"
  564.      pattern)))
  565.  
  566. (defun igrep-format-find-command (command files)
  567.   ;; Format `grep` COMMAND to be invoked via `find` on FILES.
  568.   (let ((directories '())
  569.     (patterns '()))
  570.     (while files
  571.       (let ((dir (igrep-file-directory (car files)))
  572.         (pat (igrep-file-pattern (car files))))
  573.     (if (and (not (string= dir "."))
  574.          (file-symlink-p dir))
  575.         (setq dir (concat dir "/.")))
  576.     (if (not (member dir directories))
  577.         (setq directories (cons dir directories)))
  578.     (cond ((equal pat "*")
  579.            (setq patterns t))
  580.           ((and (listp patterns)
  581.             (not (member pat patterns)))
  582.            (setq patterns (cons pat patterns)))))
  583.       (setq files (cdr files)))
  584.     (format (cond ((eq igrep-find-use-xargs 'gnu)
  585.            ;; | \\\n
  586.            "find %s %s %s %s -print0 | xargs -0 -e %s")
  587.           (igrep-find-use-xargs
  588.            ;; | \\\n
  589.            "find %s %s %s %s -print | xargs -e %s")
  590. ;;;          ((memq system-type '(ms-dos windows-95 windows-nt emx))
  591. ;;;           "find %s %s %s %s -exec %s ;")
  592.           (t
  593.            "find %s %s %s %s -exec %s \\;"))
  594.         (mapconcat (function identity) (nreverse directories)
  595.                " ")
  596.         (if igrep-find-prune-options
  597.         (format "-type d %c( %s %c) -prune -o"
  598.             (or igrep-parenthesis-escape-char ? )
  599.             igrep-find-prune-options
  600.             (or igrep-parenthesis-escape-char ? ))
  601.           "")
  602.         (if igrep-find-file-options
  603.         (format "%c( %s %c)"
  604.             (or igrep-parenthesis-escape-char ? )
  605.             igrep-find-file-options
  606.             (or igrep-parenthesis-escape-char ? ))
  607.           "")
  608.         (if (listp patterns)
  609.         (if (cdr patterns)    ; (> (length patterns) 1)
  610.             (format "%c( %s %c)"
  611.                 (or igrep-parenthesis-escape-char " ")
  612.                 (mapconcat (function (lambda (pat)
  613.                            (format "-name \"%s\"" pat)))
  614.                        (nreverse patterns)
  615.                        " -o ")
  616.                 (or igrep-parenthesis-escape-char " "))
  617.           (format "-name \"%s\"" (car patterns)))
  618.           "")
  619.         command)))
  620.  
  621. (defun igrep-read-args (&optional no-files)
  622.   ;; Read and return a list: (PROGRAM EXPRESSION FILES OPTIONS).
  623.   ;; If NO-FILES is non-nil, then FILES is not read and nil is returned
  624.   ;; in its place.
  625.   (let* ((program (igrep-read-program (if igrep-verbose-prompts
  626.                       (if igrep-find
  627.                           "[find] "))))
  628.      (prompt-prefix (if igrep-verbose-prompts
  629.                 (apply (function concat)
  630.                    (if igrep-find
  631.                        "[find] ")
  632.                    (if (eq igrep-verbose-prompts t)
  633.                        (list program " ")))))
  634.      (options (igrep-read-options prompt-prefix)))
  635.     (if (eq igrep-verbose-prompts t)
  636.     (setq prompt-prefix
  637.           (concat prompt-prefix options " ")))
  638.     (list program
  639.       (igrep-read-expression prompt-prefix)
  640.       (if (not no-files)
  641.           (igrep-read-files prompt-prefix))
  642.       options)))
  643.  
  644. (defsubst igrep-prefix (prefix string)
  645.   ;; If PREFIX is non-nil, concatenate it and STRING; otherwise, return STRING.
  646.   (if prefix
  647.       (concat prefix string)
  648.     string))
  649.  
  650. (defun igrep-read-program (&optional prompt-prefix)
  651.   ;; If igrep-program is nil, read and return a program name from the
  652.   ;; minibuffer; otherwise, return igrep-program.
  653.   ;; Optional PROMPT-PREFIX is prepended to the "Program: " prompt.
  654.   (or igrep-program
  655.       (let ((prompt "Program: "))
  656.     (completing-read (igrep-prefix prompt-prefix prompt) igrep-program-table
  657.              nil t (or igrep-program-default "grep")))))
  658.  
  659. (defun igrep-read-options (&optional prompt-prefix)
  660.   ;; If current-prefix-arg is '(4) or '(64), read and return an options
  661.   ;; string from the minibuffer; otherwise, return igrep-options.
  662.   ;; Optional PROMPT-PREFIX is prepended to the "Options: " prompt.
  663.   (if (or igrep-read-options
  664.       (and (consp current-prefix-arg)
  665.            (memq (prefix-numeric-value current-prefix-arg)
  666.              '(4 64))))
  667.       (let ((prompt "Options: "))
  668.     (read-string (igrep-prefix prompt-prefix prompt)
  669.              (or igrep-options "-")))
  670.     igrep-options))
  671.  
  672. (defun igrep-read-expression (&optional prompt-prefix)
  673.   ;; Read and return a `grep` expression string from the minibuffer.
  674.   ;; Optional PROMPT-PREFIX is prepended to the "Expression: " prompt.
  675.   (let ((default-expression (igrep-default-expression)))
  676.     (if (string= default-expression "")
  677.     (read-from-minibuffer (igrep-prefix prompt-prefix "Expression: ")
  678.                   nil nil nil 'igrep-expression-history)
  679.       (let ((expression
  680.          (igrep-read-string-with-default-in-history
  681.           (igrep-prefix prompt-prefix (format "Expression (default %s): "
  682.                           default-expression))
  683.           default-expression
  684.           'igrep-expression-history)))
  685.     (if (string= expression "")
  686.         default-expression
  687.       expression)))))
  688.  
  689. (defun igrep-default-expression ()
  690.   (if (eq major-mode 'dired-mode)
  691.       (let ((dired-file (dired-get-filename nil t)))
  692.     (save-excursion
  693.       (set-buffer (or (and dired-file (get-file-buffer dired-file))
  694.               (other-buffer (current-buffer) t)))
  695.       (current-word)))
  696.     (current-word)))
  697.  
  698. (defsubst igrep-default-key ()
  699.   ;; Return the key bound to `exit-minibuffer', preferably "\r".
  700.   (if (eq (lookup-key minibuffer-local-completion-map "\r")
  701.       (function exit-minibuffer))
  702.       "\r"
  703.     (where-is-internal (function exit-minibuffer)
  704.                minibuffer-local-completion-map
  705.                t)))
  706.  
  707. (defun igrep-read-files (&optional prompt-prefix)
  708.   ;; Read and return a file name pattern from the minibuffer.  If
  709.   ;; current-prefix-arg is '(16) or '(64), read multiple file name
  710.   ;; patterns and return them in a list.  Optional PROMPT-PREFIX is
  711.   ;; prepended to the "File(s): " prompt.
  712.   (let* ((dired-subdirectory (if (eq major-mode 'dired-mode)
  713.                  (dired-current-directory t)))
  714.      (default-files (concat dired-subdirectory
  715.                 (igrep-default-file-pattern)))
  716.      (prompt (format "File(s) (default %s): " default-files))
  717.      (insert-default-directory nil)    ; use relative path names
  718.      (file (igrep-read-file-name-with-default-in-history
  719.         (igrep-prefix prompt-prefix prompt)
  720.         default-files
  721.         nil
  722.         'igrep-files-history)))
  723.     (if (or igrep-read-multiple-files
  724.         (and (consp current-prefix-arg)
  725.          (memq (prefix-numeric-value current-prefix-arg)
  726.                '(16 64))))
  727.     (let ((files (list file)))
  728.       (setq prompt
  729.         (igrep-prefix prompt-prefix
  730.                   (if igrep-verbose-prompts
  731.                   (format "File(s): [Type `%s' when done] "
  732.                       (key-description (igrep-default-key)))
  733.                 "File(s): ")))
  734.       (while (not (string= (setq file
  735.                      (igrep-read-file-name prompt
  736.                                nil "" nil nil
  737.                                'igrep-files-history))
  738.                    ""))
  739.         (setq files (cons file files)))
  740.       (nreverse files))
  741.       file)))
  742.  
  743. (defmacro igrep-with-default-in-history (default history &rest body)
  744.   ;; Temporarily append DEFAULT to HISTORY, and execute BODY forms.
  745.   (` (progn
  746.        ;; Append default to history:
  747.        (set history
  748.         (cons (, default)
  749.           (if (boundp (, history))
  750.               (symbol-value (, history))
  751.             '())))
  752.        (unwind-protect            ; Make sure the history is restored.
  753.        ;; Execute body:
  754.        (progn (,@ body))
  755.      ;; Delete default from history (undo the append above):
  756.      (setcdr (symbol-value (, history))
  757.          (nthcdr 2 (symbol-value (, history))))))))
  758.  
  759. (defun igrep-read-string-with-default-in-history (prompt default history)
  760.   ;; Read a string from the minibuffer, prompting with string PROMPT.
  761.   ;; DEFAULT can be inserted into the minibuffer with `previous-
  762.   ;; history-element'; HISTORY is a symbol whose value (if bound) is a
  763.   ;; list of previous results, most recent first.
  764.   (let ((string (igrep-with-default-in-history default history
  765.           (read-from-minibuffer prompt nil nil nil history))))
  766.     ;; Replace empty string in history with default:
  767.     (if (string= string "")
  768.     (setcar (symbol-value history) default))
  769.     string))
  770.  
  771. (defun igrep-read-file-name-with-default-in-history
  772.   (prompt &optional default initial history)
  773.   ;; Read a file name from the minibuffer, prompting with string PROMPT.
  774.   ;; DEFAULT can be inserted into the minibuffer with `previous-
  775.   ;; history-element'; HISTORY is a symbol whose value (if any) is a
  776.   ;; list of previous results, most recent first.
  777.   (igrep-with-default-in-history default history
  778.     (igrep-read-file-name prompt nil default nil initial history)))
  779.  
  780. (defun igrep-read-file-name (prompt
  781.   &optional directory default existing initial history)
  782.   ;; Just like read-file-name, but with optional HISTORY.
  783.   ;; Also: convert DIRECTORY to DIRECTORY/* file name pattern.
  784.   (let ((file-name
  785.      (if history
  786.          (let ((file-name-history (symbol-value history)))
  787.            (prog1 (read-file-name prompt directory default existing initial)
  788.          (set history file-name-history)))
  789.        (read-file-name prompt directory default existing initial))))
  790.     (if (and (not (string-equal file-name ""))
  791.          (file-directory-p file-name))
  792.     (expand-file-name "*" file-name)
  793.       file-name)))
  794.  
  795. (defun igrep-default-file-pattern ()
  796.   ;; Return a shell file name pattern that matches files with the same
  797.   ;; extension as the file being visited in the current buffer.
  798.   ;; (Based on other-possibly-interesting-files in ~/as-is/unix.el, by
  799.   ;; Wolfgang Rupprecht <wolfgang@mgm.mit.edu>.)
  800.   (if (eq major-mode 'dired-mode)
  801.       (cond ((stringp dired-directory)
  802.          (if (file-directory-p dired-directory)
  803.          "*"
  804.            (file-name-nondirectory dired-directory))) ; wildcard
  805.         ((consp dired-directory)    ; (DIR FILE ...)
  806.          (mapconcat 'identity (cdr dired-directory) " ")))
  807.     (if buffer-file-name
  808.     (let ((file-name (file-name-nondirectory buffer-file-name)))
  809.       (concat "*"
  810.           (save-match-data
  811.             (if (string-match "\\.[^.]+\\(\\.g?[zZ]\\)?$" file-name)
  812.             (substring file-name
  813.                    (match-beginning 0) (match-end 0))))))
  814.       "*")))
  815.  
  816. ;;; Local Variables:
  817. ;;; eval: (put 'igrep-with-default-in-history 'lisp-indent-hook 2)
  818. ;;; eval: (put 'igrep-define 'lisp-indent-hook 1)
  819. ;;; eval: (put 'igrep-find-define 'lisp-indent-hook 1)
  820. ;;; End:
  821.  
  822. ;;;; igrep.el ends here
  823.  
  824.